#include "rrclipping.h"
#include <cstdio>

RRClipping::RRClipping()
{
    Xmax = 1.0f; Xmin = -1.0f;
    Ymax = 1.0f; Ymin = -1.0f;
}

uint8_t RRClipping::rrOutCode(float x, float y)
{
    uint8_t o;
    o = 0b0000;
    if(y > Ymax) o |= 0b1000;
    else if(y < Ymin) o |= 0b0100;
    if(x > Xmax) o |= 0b0010;
    else if(x < Xmin) o |= 0b0001;

    return o;
}

void RRClipping::rrClipping(RRPrim *src, RRPrim *dest)
{
    RRVertex s, t;
    RRPrimType tprim;
    int cnt;

    dest->rrReset();
    prim = dest;
    tprim = src->rrGetType();
    cnt = src->rrGetVertsNum();
    if(tprim == RR_LINE || tprim == RR_LINE_STRIP) {
        dest->rrSetType(tprim);
        for(int i=0; i<cnt-1; i++) {
            s = src->rrGetVert(i);
            t = src->rrGetVert(i+1);
            rrCohenSutherland(s, t, s.x, s.y, t.x, t.y);
        }
    }
    else if(tprim == RR_TRIANGLE) {
        rrSutherlandHodgman(src);
        dest->rrSetType(RR_POLYGON);
    }
}

void RRClipping::rrCohenSutherland(RRVertex s, RRVertex t, float Xp, float Yp, float Xq, float Yq)
{
    uint8_t o1, o2;
    o1 = rrOutCode(Xp, Yp);
    o2 = rrOutCode(Xq, Yq);
    //printf("%d %d\n", o1, o2);

    float X, Y;
    float t1, t2;
    RRVertex V;
    if(o1 == 0 && o2 == 0) {
        // primitiveに追加
        if(Xp == Xq && Yp == Yq) {	// 一点のみ
            // 内挿
            t1 = ((Xp - s.x) * (t.x - s.x) + (Yp - s.y) * (t.y - s.y)) / ((t.x - s.x) * (t.x - s.x) + (t.y - s.y) * (t.y - s.y));
            V.x = Xp; V.y = Yp; V.z = (1 - t1) * s.z + t1 * t.z;
            V.w = (1 - t1) * s.w + t1 * t.w;
            for(int i = 0; i<4; i++)
                V.c[i] = (1 - t1) * s.c[i] + t1 * t.c[i];
            prim->rrSetVert(V);
        }
        else {
            // それぞれの点の要素に内挿した値を入れる
            t1 = ((Xp - s.x) * (t.x - s.x) + (Yp - s.y) * (t.y - s.y)) / ((t.x - s.x) * (t.x - s.x) + (t.y - s.y) * (t.y - s.y));
            t2 = ((Xq - s.x) * (t.x - s.x) + (Yq - s.y) * (t.y - s.y)) / ((t.x - s.x) * (t.x - s.x) + (t.y - s.y) * (t.y - s.y));
            V.x = Xp; V.y = Yp; V.z = (1 - t1) * s.z + t1 * t.z;
            V.w = (1 - t1) * s.w + t1 * t.w;
            for(int i = 0; i<4; i++)
                V.c[i] = (1 - t1) * s.c[i] + t1 * t.c[i];
            prim->rrSetVert(V);
            V.x = Xq; V.y = Yq; V.z = (1 - t2) * s.z + t2 * t.z;
            V.w = (1 - t2) * s.w + t2 * t.w;
            for(int i = 0; i<4; i++)
                V.c[i] = (1 - t2) * s.c[i] + t2 * t.c[i];
            prim->rrSetVert(V);
        }
    }
    else if((o1 & o2) != 0) {
        // 範囲外
    }
    else {
        // o1のクリッピング
        if((o1 & 0b1000) != 0) {	// o1上切り取り
            if(Yp != Yq)
                X = Xq + (Xp - Xq) * (Ymax - Yq) / (Yp - Yq);
            else
                X = Xq;
            Y = Ymax;
            rrCohenSutherland(s, t, X, Y, Xq, Yq);
        }
        else if((o1 & 0b0100) != 0) {	// o1下切り取り
            if(Yp != Yq)
                X = Xq + (Xp - Xq) * (Ymin - Yq) / (Yp - Yq);
            else
                X = Xq;
            Y = Ymin;
            rrCohenSutherland(s, t, X, Y, Xq, Yq);
        }
        else if((o1 & 0b0010) != 0) {	// o1右切り取り
            if(Xp != Xq)
                Y = Yq + (Yp - Yq) * (Xmax - Xq) / (Xp - Xq);
            else
                Y = Yq;
            X = Xmax;
            //printf("hell0 %f %f\n", X, Y);
            rrCohenSutherland(s, t, X, Y, Xq, Yq);
        }
        else if((o1 & 0b0001) != 0) {	// o1左切り取り
            if(Xp != Xq)
                Y = Yq + (Yp - Yq) * (Xmin - Xq) / (Xp - Xq);
            else
                Y = Yq;
            X = Xmin;
            rrCohenSutherland(s, t, X, Y, Xq, Yq);
        }
        // o2のクリッピング
        else if((o2 & 0b1000) != 0) {	// o2上切り取り
            if(Yq != Yp)
                X = Xp + (Xq - Xp) * (Ymax - Yp) / (Yq - Yp);
            else
                X = Xp;
            Y = Ymax;
            rrCohenSutherland(s, t, Xp, Yp, X, Y);
        }
        else if((o2 & 0b0100) != 0) {	// o2下切り取り
            if(Yq != Yp)
                X = Xp + (Xq - Xp) * (Ymin - Yp) / (Yq - Yp);
            else
                X = Xp;
            Y = Ymin;
            rrCohenSutherland(s, t, Xp, Yp, X, Y);
        }
        else if((o2 & 0b0010) != 0) {	// o2右切り取り
            if(Xq != Xp)
                Y = Yp + (Yq - Yp) * (Xmax - Xp) / (Xq - Xp);
            else
                Y = Yp;
            X = Xmax;
            rrCohenSutherland(s, t, Xp, Yp, X, Y);
        }
        else if((o2 & 0b0001) != 0) {	// o2左切り取り
            if(Xq != Xp)
                Y = Yp + (Yq - Yp) * (Xmin - Xp) / (Xq - Xp);
            else
                Y = Yp;
            X = Xmin;
            rrCohenSutherland(s, t, Xp, Yp, X, Y);
        }
    }
}

void RRClipping::rrSutherlandHodgman(RRPrim *src)
{
    //puts("Im in SH");
    prim_bottom.rrReset();
    prim_right.rrReset();
    prim_top.rrReset();
    RRVertex s, t;
    int vnum;
    vnum = src->rrGetVertsNum();
    for(int i=0; i<vnum-1; i++) {
        s = src->rrGetVert(i);
        t = src->rrGetVert(i+1);
        rrClipEdge(s, t, RR_BOTTOM);
    }
    s = src->rrGetVert(vnum-1);
    t = src->rrGetVert(0);
    rrClipEdge(s, t, RR_BOTTOM);

    vnum = prim_bottom.rrGetVertsNum();
    for(int i=0; i<vnum-1; i++) {
        s = prim_bottom.rrGetVert(i);
        t = prim_bottom.rrGetVert(i+1);
        rrClipEdge(s, t, RR_RIGHT);
    }
    s = prim_bottom.rrGetVert(vnum-1);
    t = prim_bottom.rrGetVert(0);
    rrClipEdge(s, t, RR_RIGHT);

    vnum = prim_right.rrGetVertsNum();
    for(int i=0; i<vnum-1; i++) {
        s = prim_right.rrGetVert(i);
        t = prim_right.rrGetVert(i+1);
        rrClipEdge(s, t, RR_TOP);
    }
    s = prim_right.rrGetVert(vnum-1);
    t = prim_right.rrGetVert(0);
    rrClipEdge(s, t, RR_TOP);

    vnum = prim_top.rrGetVertsNum();
    for(int i=0; i<vnum-1; i++) {
        s = prim_top.rrGetVert(i);
        t = prim_top.rrGetVert(i+1);
        rrClipEdge(s, t, RR_LEFT);
    }
    s = prim_top.rrGetVert(vnum-1);
    t = prim_top.rrGetVert(0);
    rrClipEdge(s, t, RR_LEFT);

}

void RRClipping::rrClipEdge(RRVertex s, RRVertex t, RRClipDir dir)
{
    float X, Y;
    if(dir == RR_BOTTOM) {
        if(s.y < Ymin  && t.y >= Ymin) {
            if(s.y != t.y)
                X = t.x + (s.x - t.x) * (Ymin - t.y) / (s.y - t.y);
            else
                X = t.x;
            Y = Ymin;
            rrComputeInterpolation(s, t, X, Y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
        else if(s.y >= Ymin && t.y < Ymin) {
            if(s.y != t.y)
                X = t.x + (s.x - t.x) * (Ymin - t.y) / (s.y - t.y);
            else
                X = t.x;
            Y = Ymin;
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, X, Y, dir);
        }
        else if(s.y < Ymin && t.y < Ymin) {}
        else {
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
    }
    else if(dir == RR_RIGHT) {
        if(s.x > Xmax  && t.x <= Xmax) {
            if(s.x != t.x)
                Y = t.y + (s.y - t.y) * (Xmax - t.x) / (s.x - t.x);
            else
                Y = t.y;
            X = Xmax;
            rrComputeInterpolation(s, t, X, Y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
        else if(s.x <= Xmax && t.x > Xmax) {
            if(s.x != t.x)
                Y = t.y + (s.y - t.y) * (Xmax - t.x) / (s.x - t.x);
            else
                Y = t.y;
            X = Xmax;
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, X, Y, dir);
        }
        else if(s.x > Xmax && t.x > Xmax) {}
        else {
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
    }
    else if(dir == RR_TOP) {
        if(s.y > Ymax  && t.y <= Ymax) {
            if(s.y != t.y)
                X = t.x + (s.x - t.x) * (Ymax - t.y) / (s.y - t.y);
            else
                X = t.x;
            Y = Ymax;
            rrComputeInterpolation(s, t, X, Y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
        else if(s.y <= Ymax && t.y > Ymax) {
            if(s.y != t.y)
                X = t.x + (s.x - t.x) * (Ymax - t.y) / (s.y - t.y);
            else
                X = t.x;
            Y = Ymax;
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, X, Y, dir);
        }
        else if(s.y > Ymax && t.y > Ymax) {}
        else {
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }

    }
    else if(dir == RR_LEFT) {
        if(s.x < Xmin  && t.x >= Xmin) {
            if(s.x != t.x)
                Y = t.y + (s.y - t.y) * (Xmin - t.x) / (s.x - t.x);
            else
                Y = t.y;
            X = Xmin;
            rrComputeInterpolation(s, t, X, Y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }
        else if(s.x >= Xmin && t.x < Xmin) {
            if(s.x != t.x)
                Y = t.y + (s.y - t.y) * (Xmin - t.x) / (s.x - t.x);
            else
                Y = t.y;
            X = Xmin;
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, X, Y, dir);
        }
        else if(s.x < Xmin && t.x < Xmin) {}
        else {
            rrComputeInterpolation(s, t, s.x, s.y, dir);
            rrComputeInterpolation(s, t, t.x, t.y, dir);
        }

    }
}

void RRClipping::rrComputeInterpolation(RRVertex s, RRVertex t, float X, float Y, RRClipDir dir)
{
    // 内挿
    RRVertex V, W, O;
    float t1;
    t1 = ((X - s.x) * (t.x - s.x) + (Y - s.y) * (t.y - s.y)) / ((t.x - s.x) * (t.x - s.x) + (t.y - s.y) * (t.y - s.y));
    V.x = X; V.y = Y; V.z = (1 - t1) * s.z + t1 * t.z;
    V.w = (1 - t1) * s.w + t1 * t.w;
    for(int i = 0; i<4; i++)
        V.c[i] = (1 - t1) * s.c[i] + t1 * t.c[i];
    switch(dir) {
    case RR_BOTTOM:
        W = prim_bottom.rrGetCurrentVert();
        O = prim_bottom.rrGetVert(0);
        if((W.x != V.x || W.y != V.y) && (O.x != V.x || O.y != V.y))
            prim_bottom.rrSetVert(V);
        break;
    case RR_RIGHT:
        W = prim_right.rrGetCurrentVert();
        O = prim_right.rrGetVert(0);
        if((W.x != V.x || W.y != V.y) && (O.x != V.x || O.y != V.y))
            prim_right.rrSetVert(V);
        break;
    case RR_TOP:
        W = prim_top.rrGetCurrentVert();
        O = prim_top.rrGetVert(0);
        if((W.x != V.x || W.y != V.y) && (O.x != V.x || O.y != V.y))
            prim_top.rrSetVert(V);
        break;
    case RR_LEFT:
        W = prim->rrGetCurrentVert();
        O = prim->rrGetVert(0);
        if((W.x != V.x || W.y != V.y) && (O.x != V.x || O.y != V.y))
            prim->rrSetVert(V);
        break;
    default:
        break;
    }
}

